﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using VideoLink.Properties;
using System.Threading;
using VideoLink.Links;
using VideoLink.Frames;
using VideoLink.Utility;
using System.Diagnostics;
using System.Runtime.Serialization;

namespace VideoLink
{
    /// <summary>
    /// Главная форма приложения.
    /// </summary>
    public partial class VideoLinkForm : Form
    {
        #region Private fields

        /// <summary>
        /// Соединение.
        /// </summary>
        private ILink link;

        /// <summary>
        /// Счётчик статистики.
        /// </summary>
        private Statistics stat;

        /// <summary>
        /// Показывает, сколько байт было принято между двумя тиками
        /// sendFrameTimer
        /// </summary>
        private long bytesReceived;

        #endregion

        /// <summary>
        /// Создать новый экземпляр класса по умолчанию.
        /// </summary>
        public VideoLinkForm()
        {
            Settings.Default.Reload();

            InitializeComponent();

            stat = new Statistics(statisticsPG);
            DebugLog.Initialize(debugTB);

            LoadCustomSettings();
        }

        #region Link

        /// <summary>
        /// Установить подключение
        /// </summary>
        private void InitializeLink()
        {
            link = LinkLoader.GetLink(Settings.Default.OtherSideIP,
                Path.GetFullPath(Settings.Default.SmartLinkAssemblyName),
                Settings.Default.SmartLinkClassName);

            var res = link.OpenDevice();
            if (res != 0)
                Utils.ShowError("OpenDevice returned: " + res.ToString(),
                    "Device open error!");

            link.NewFileReceived += new NewFileHandler(link_NewFileReceived);
            link.GetStatus += new TransieveStatusHandler(link_GetStatus);
            link.SendStatus += new TransieveStatusHandler(link_SendStatus);
        }

        /// <summary>
        /// Вызывается при изменении состояния процесса передачи файла.
        /// Пока что не делается ничего.
        /// </summary>
        /// <param name="finished">
        /// 1 - если передача завершена, 0 в противном случае.
        /// </param>
        /// <param name="progress">
        /// Процент переданных данных от всего объёма файла.
        /// </param>
        private void link_SendStatus(int finished, int progress)
        {

        }

        /// <summary>
        /// Вызывается при изменении состояния процесса получения файла.
        /// Пока что не делается ничего.
        /// </summary>
        /// <param name="finished">
        /// 1 - если получение завершено, 0 в противном случае.
        /// </param>
        /// <param name="progress">
        /// Процент полученных данных от всего объёма файла.
        /// </param>
        private void link_GetStatus(int finished, int progress)
        {

        }

        /// <summary>
        /// Вызывается при получении нового файла.
        /// На момент вызова события файл уже готов к использованию.
        /// </summary>
        /// <param name="fileName">
        /// Имя полученного файла.
        /// </param>
        private void link_NewFileReceived(string fileName)
        {
            BeginInvoke(new Action(() =>
                {
                    try
                    {
                        var justReceived = 0L;
                        var frame = Utils.Deserialize(fileName, out justReceived) as Frame;

                        bytesReceived += justReceived;
                        
                        videosPanel.NewFrameReceived(frame);
                        
                        if (File.Exists(fileName))
                            File.Delete(fileName);
                    }
                    catch (SerializationException)
                    {
                        DebugLog.Instance.WriteLine("SerializationException occured during " + 
                            "loading received file");
                    }
                }));
        }

        #endregion

        #region Video management

        /// <summary>
        /// Обработчик клика по пункту меню "Добавить (покадрово)"
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        private void addByFrameMI_Click(object sender, EventArgs e)
        {
            videosPanel.AddNewByFrame();
        }

        /// <summary>
        /// Обработчик клика по пункту меню "Добавить (целиком)"
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        private void addEntireMI_Click(object sender, EventArgs e)
        {
            videosPanel.AddNewEntire();
        }

        /// <summary>
        /// Обработчик клика по пункту меню "Добавить поток JPEG"
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        private void addJPEGMI_Click(object sender, EventArgs e)
        {
            videosPanel.AddNewJPEGStream();
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Загрузить и применить настройки приложения
        /// </summary>
        private void LoadCustomSettings()
        {
            logMI.Checked = Settings.Default.ShowLog;
            statMI.Checked = Settings.Default.ShowStatistics;
        }

        /// <summary>
        /// Сохранить изменения в настройках приложения
        /// </summary>
        private void SaveCustomSettings()
        {
            Settings.Default.ShowLog = logMI.Checked;
            Settings.Default.ShowStatistics = statMI.Checked;
            Settings.Default.Save();
        }

        /// <summary>
        /// Послать frame
        /// </summary>
        /// <param name="frame">Состояние исходящих потоков.</param>
        private void SendFrame(Frame frame)
        {
            var fileName = "out_" + Process.GetCurrentProcess().Id.ToString()
                + DateTime.Now.ToString("_HH_mm_ss_ffffff") + ".dat";

            var bytesSent = Utils.Serialize(fileName, frame);

            stat.BytesSent(bytesSent);

            link.SendFile(fileName);

            File.Delete(fileName);
        }

        #endregion

        #region Form events' handlers

        private void sendFrameTimer_Tick(object sender, EventArgs e)
        {
            if (!link.Active)
            {
                connectionStateSL.Text = "Не подключено";
                return;
            }
            else
            {
                connectionStateSL.Text = "Подключено";
            }

            stat.BytesReceived(bytesReceived);
            bytesReceived = 0;

            var frame = videosPanel.GetFrame();

            if (frame.ChangesCount < 1)
            {
                stat.BytesSent(0);
                return;
            }

            SendFrame(frame);
        }

        private void VideoLinkForm_Load(object sender, EventArgs e)
        {
            DebugLog.Instance.WriteWithTime("Started!");

            InitializeLink();
        }

        private void VideoLinkForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            videosPanel.CloseStreams();

            link.CloseDevice();

            SaveCustomSettings();
        }

        private void statMI_CheckedChanged(object sender, EventArgs e)
        {
            vertSC.Panel2Collapsed = !statMI.Checked;
        }

        private void debugMI_CheckedChanged(object sender, EventArgs e)
        {
            mainSC.Panel2Collapsed = !logMI.Checked;
        }

        #endregion
    }
}
